﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity;
using QuickPrompt.DataAccess;
using QuickPrompt.DomainModel;
using QuickPromt.Service;
using RedandBlue.Common.Logging;
using RedandBlue.RepositoryPattern.EF6;
using RedandBlue.RepositoryPattern.Repositories;
using RedandBlue.RepositoryService;

namespace QuickPrompt.BusinessService
{
    public class QuickNoteService : Service<QuickNote>, IQuickNoteService
    {
        private readonly IRepositoryAsync<QuickNote> _repository;
        private readonly IQuickPromptService _quickPromptService;
        private readonly TimeSpan _prompTimeSpan = new TimeSpan(9, 0, 0);
        public QuickNoteService(IRepositoryAsync<QuickNote> repository)
            : base(repository)
        {
            _repository = repository;
            var dataContext = new QuickPromptContext();
            _quickPromptService = new QuickPromptService(new Repository<Prompt>(dataContext, new UnitOfWork(dataContext)));
        }

        public IEnumerable<QuickNote> Get(bool excludeDeleted = true)
        {
            return !excludeDeleted 
                ? base.Query().Include(x => x.Prompts).Include(x => x.User).Select().ToList() 
                : base.Query(x => !x.IsDeleted).Include(x=>x.Prompts).Include(x=>x.User).Select().ToList();
        }

        public IEnumerable<QuickNote> Get(string userGuid, bool excludeDeleted = true)
        {
            return !excludeDeleted 
                ? base.Query(x => x.UserGuid == userGuid).Include(x => x.Prompts).Include(x => x.User).Select().ToList() 
                : base.Query(x => x.UserGuid == userGuid && x.IsDeleted == false).Include(x => x.Prompts).Include(x => x.User).Select().ToList();
        }

        public IEnumerable<QuickNote> FetchReadyOnes()
        {
            return
                base.Query(x => x.IsActive
                                && x.Prompts.Any()
                                && x.EndDateUtc > DateTime.UtcNow
                                && x.StartDateUtc <= DateTime.UtcNow)
                    .Include(x => x.Prompts)
                    .Include(x => x.User)
                    .Select()
                    .ToList();
        }

        public QuickNote GetNote(int noteId)
        {
            return
                _repository.Query(x => x.Id == noteId)
                    .Include(x => x.Prompts)
                    .Include(x => x.User)
                    .Select()
                    .SingleOrDefault();
        }

        public void QueuePrompts()
        {
            var activeNotes = FetchReadyOnes();
            var quickNotes = activeNotes as QuickNote[] ?? activeNotes.ToArray();

            var activePrompts = (from a in quickNotes
                                 from p in a.Prompts
                                 select p).ToList();

            Parallel.ForEach(activePrompts, prompt =>
            {
                switch (prompt.PromptFrequency)
                {
                    case PromptFrequency.Weekly:
                        if (DateTime.UtcNow.TimeOfDay == _prompTimeSpan
                            && prompt.StartingFromUtc.DayOfWeek == DateTime.UtcNow.DayOfWeek)
                            _quickPromptService.QuickPrompt(prompt);
                        break;
                    case PromptFrequency.BiWeekly:
                        List<DateTime> biweeklyDateTimes = new List<DateTime>();
                        DateTime biweeklyDateTime = prompt.QuickNote.StartDateUtc;
                        while (prompt.QuickNote.EndDateUtc != DateTime.MaxValue
                               && biweeklyDateTime <= prompt.QuickNote.EndDateUtc)
                        {
                            biweeklyDateTimes.Add(biweeklyDateTime);
                            biweeklyDateTime = biweeklyDateTime.AddDays(14);
                        }
                        if (biweeklyDateTimes.Any(x => x.Date.Equals(DateTime.UtcNow.Date))
                            && DateTime.UtcNow.TimeOfDay == _prompTimeSpan)
                            _quickPromptService.QuickPrompt(prompt);
                        break;
                    case PromptFrequency.Monthly:
                        if (DateTime.UtcNow.TimeOfDay == _prompTimeSpan
                            && prompt.StartingFromUtc.Day == DateTime.UtcNow.Day)
                            _quickPromptService.QuickPrompt(prompt);
                        break;
                    case PromptFrequency.Daily:
                        if (DateTime.UtcNow.TimeOfDay == _prompTimeSpan)
                            _quickPromptService.QuickPrompt(prompt);
                        break;
                    case PromptFrequency.OneTime:
                        double minuteDifference = DateTime.UtcNow.Subtract(prompt.StartingFromUtc).TotalMinutes;
                        if (DateTime.UtcNow.DayOfYear == prompt.StartingFromUtc.DayOfYear
                            && -1 <= minuteDifference && minuteDifference <= 1)
                            _quickPromptService.QuickPrompt(prompt);
                        break;
                }
            });
        }

        public void SetToDelete(int id)
        {
            var note = base.Query(x => x.Id == id).Select().SingleOrDefault();
            if (note == null)
            {
                Log.Error(GetType().FullName, MethodBase.GetCurrentMethod().Name, "No Note found with {0}", id);
                return;
            }
            note.IsDeleted = true;
            // e.g. add business logic here before inserting
            base.Update(note);
        }

        public override void Update(QuickNote entity)
        {
            var note = base.Query(x=>x.Id == entity.Id).Select().SingleOrDefault();
            if (note == null)
            {
                Log.Error(GetType().FullName, MethodBase.GetCurrentMethod().Name, "No Note found with {0}", entity.Id);
                return;
            }
            note.Name = entity.Name;
            note.Description = entity.Description;
            note.StartDateUtc = entity.StartDateUtc.ToUniversalTime();
            note.EndDateUtc = entity.EndDateUtc.ToUniversalTime();
            // e.g. add business logic here before inserting
            base.Update(note);
        }
        public override void Insert(QuickNote entity)
        {
            entity.UserGuid = HttpContext.Current.User.Identity.GetUserId();
            if (string.IsNullOrEmpty(entity.UserGuid))
            {
                Log.Error(GetType().FullName,MethodBase.GetCurrentMethod().Name,"No user is logged in");
                return;
            }
            entity.StartDateUtc = entity.StartDateUtc.ToUniversalTime();
            entity.EndDateUtc = entity.EndDateUtc.ToUniversalTime();
            // e.g. add business logic here before inserting
            base.Insert(entity);
        }

        public override void Delete(object id)
        {
            // e.g. add business logic here before deleting
            base.Delete(id);
        }
    }
}
